Appearance
实战篇 5-商品详情页面开发
商品详情页面开发
上一章我们已经学习了列表页的开发,从列表页面里选择感兴趣的商品,进入商品详情页,查看商品的相关信息。本章我们主要讲商品详情页的开发。
商品详情页在电商应用里扮演一个重要的角色,它可以是简单的也可以是复杂的。说重要是因为它是黄金购物流程上的重要一环,首页、搜索页、列表页、营销活动最后的落地页都是商品详情页;简单的可以是由几张商品图和文字说明及加一个「加入购物车」按钮或者一个「去结算」按钮就算详情页了,复杂的详情页,包含页面展示商品图、商品视频、商品属性、商品介绍、评论、商品的关联商品、商品套餐、预售、抢购、历史浏览、推荐购买等,非常复杂。
页面布局
本项目的商品详情页比较简单,只有展示商品图、商品视频、商品属性、商品介绍,没有预售和抢购状态,也没有评论,我们先看一下效果图。
按模块分有商品主图、介绍、店铺信息、加车浮层四个模块,店铺信息模块比较简单,我们不讲解,其余三个模块我们逐个讲解。
商品主图模块
商品主图是一个轮播模块,有商品图和视频组成,其中商品图又有长图和方图。小程序有个 Swiper 组件,可以方便地实现该需求。
<Swiper className='detail_swiper'
indicator-dots='true'
indicator-color='#ddd'
indicator-active-color='#232323'
current='0'
interval='3000'
duration='300'
circular='true'
onChange={this.swpierChange} >
{srcs.map((item) => {
return <SwiperItem className='detail_swiper_item'>
<Image src={item} onClick={this.swiperClick} mode='widthFix' />
</SwiperItem>
})}
{mainVideoImgUrl && <SwiperItem className='detail_swiper_item detail_swiper_item_video' >
{this.state.isPlayVideo ? <Video className={ isIpx ? 'detail_video detail_video_ipx' : 'detail_video' } src={mainVideoPath} autoplay='true' show-fullscreen-btn='true' /> : <Image className='detail_video_cover' onClick={this.playVideo} src={mainVideoImgUrl} mode='widthFix' />}
</SwiperItem>}
</Swiper>
这个模块宽高是 750x960,在 iPhone X 下,是全屏的宽度,按等比例缩放在高度也是恰到好处可以看到商品的标题和价格,但是在非 iPhone X 机器下高度就差不多一屏了,首屏看不到商品的标题和价格,为了更好地体验,两边留白 40px ,宽高为 670x860。
轮播模块放视频,会存在一些问题,从文档里 Video 组件 说明的底部的 Bug&Tip 可以看到:
- Tip: Video 组件是由客户端创建的原生组件,它的层级是最高的,不能通过 z-index 控制层级。
- Tip: 请勿在 scroll-view、Swiper、picker-view、movable-view 中使用 Video 组件。
- Tip: CSS 动画对 Video 组件无效。
文档里没有说为什么不能在 Swiper 里用,所以只能自己尝试了,试了一下才发现,如果在 Swiper 里使用就会导致视频一直存在,也是符合 Tip 1 里所说的 Video 组件是由客户端创建的原生组件,它的层级最高,所以已经超出了 Swiper 组件本身了。
那我们有没有办法实现业务方的需求呢?我们看一下视频在 Swiper 下的 Bug,发现即使视频一直存在也是只存在于 Swiper 区域,那么我们控制它显示和隐藏是不是就可以达到目的。最后的实现是渲染主图轮播的时候,在显示视频的那一帧放入一张视频封面,点击封面的时候,播放视频,切换轮播的时候关闭视频显示封面。
// jsx
<Swiper className='detail_swiper' onChange={this.swpierChange} >
{mainVideoImgUrl && <SwiperItem className='detail_swiper_item detail_swiper_item_video' >
{this.state.isPlayVideo ? <Video className={ isIpx ? 'detail_video detail_video_ipx' : 'detail_video' } src={mainVideoPath} autoplay='true' show-fullscreen-btn='true' /> : <Image className='detail_video_cover' onClick={this.playVideo} src={mainVideoImgUrl} mode='widthFix' />}
</SwiperItem>}
</Swiper>
// js 切换的时候把视频关了
swpierChange = (e) => {
if (this.state.isPlayVideo) {
this.setState({isPlayVideo: false})
}
}
商品介绍
商品介绍分四个小模块:商品描述、编辑笔记、尺寸说明、服务说明,需要控制它保证只有一个模块的状态是打开的,这个比较简单。
this.state = {
detailInfoRow: [
{
name: 'desc',
title: '商品描述',
open: true
},
{
name: 'editor',
title: '编辑笔记',
open: false
},
{
name: 'size',
title: '尺寸说明',
open: false
},
{
name: 'service',
title: '服务说明',
open: false
}
]
}
// jsx
{detailInfoRowCopy.map((item, idx) => {
return <View className='detail_intro'>
{item.show && <View className={'detail_intro_row ' + item.name}>
<View className='detail_intro_hd' data-id={item.name} onClick={this.toggleFold.bind(this, idx)}>
<Text className='detail_intro_title'>
{item.title}
</Text>
<Text className='detail_intro_title_icon'>
{item.open ? '-':'+'}
</Text>
</View>
{item.open && idx < 3 && <View className='detail_intro_bd'>
...内容
</View>}
</View>
})}
// 控制显隐
toggleFold = (idx, e) => {
this.state.detailInfoRow[idx].open = !this.state.detailInfoRow[idx].open
this.setState({})
}
加车浮层
加车浮层看着简单,要展示的内容还是挺丰富的,我们先来看效果图:
了解一下模块的交互,然后我们挑几个要点来讲:
- 点击加入购物车唤起浮层
- 浮层有商品信息区:显示小图、价格、商品编号
- 浮层有地区及运费,自营运费固定,非自营运费由接口取得
- 优惠券信息,显示当前商品可用优惠券,有多张优惠券时,最多展示 2行,点击券说明下拉展示全部优惠券,每个券占一行,每行展示满减金额及使用限制及限制说明文案。无可用券则隐藏,可用优惠券这一区域
- 销售属性,按类目显示参数名称「颜色」「尺码」等;单 SKU 时显示 SKU 的参数并选中;SPU 时当前 SKU 的参数为选中状态,从搜索或列表进入时默认选中主 SKU,其他 SKU 的参数可选和不可选,无货或参数不全时不可选;点击可点参数时相当于切换 SKU,层内商品图、价格、编号 、运费、优惠券数据刷新
- 尺码有三种状态:有货未选择:“黑字白底”按钮,有货已选择:“白字黑底”按钮,无货未选择:“深灰字浅灰底”按钮
- 关闭加车浮层,判断层内 SKU 与层外是否一至,不一至则刷新
浮层
唤起加车浮层的时候,我们在页面里使用一个 View 来做背景半透明的蒙层,当点击浮层以外的区域就把浮层关闭,其实浮层以外的区域就是蒙层,我们可以在蒙层上设置点击事件,点击就关闭浮层。
唤起浮层出现蒙层,然后上下滑动页面,页面还是会滚动的,因为页面的内容大于一屏,存在滚动条,要处理这个问题需要在根节点使用 ScrollView
组件把内容包起来,然后当唤起浮层的时候设置 scrollY
为 false
。这样当出现浮层的时候,整个页面就不能滚动了。
<ScrollView style={viewStyle} scrollY={!this.state.addCartLayer} enableBackToTop={true}>
<View>这里是内容......</View>
</ScrollView>
商品属性切换
当我们切换商品的颜色的时候,其实是切换了一个新的商品,而商品的唯一 ID 就是 skuId
, 因此商品属性我们把 skuId
打在选项 Radio
的属性上,当点击改选项的时候获取该点击的对象里的值,然后拉取新的数据重新渲染浮层里的内容。
// jsx
<RadioGroup className='radiogroup' onChange={this.spuChange}>
{item.props && item.props.map(prop => {
return <Label className={prop.className}>
<Radio className='radio' value={prop.skuId} checked={prop.selected} />
{prop.name}
</Label>
}) }
</RadioGroup>
// js
spuChange (e) => {
let skuId = e.detail.value // 拿到skuId
}
属性切换的时候数据发生变化,按钮的样式也会跟着变化,按钮有三个状态:正常的灰边白色背景黑色文字,选中态为黑背景白字,无货的状态为灰背景灰字,稍微复杂,我们使用样式的类名来控制。
小结
商品的信息都是从接口拉取出来然后展示,虽然比较丰富,但都是展示型的内容,相对来说还是比较简单,这里就不一一介绍了,大家可以移步 Demo 看看实现代码。
在下一章节中我们将介绍购物车的开发。